package com.csx.framework.springboot.sample.event;

import org.springframework.boot.context.event.*;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.stereotype.Component;

/**
 * Spring内置事件
 * @author cuisongxu
 * @date 2024/2/27 周二 13:00
 */
@Component
public class SpringInnerApplicationEvent implements ApplicationListener<ApplicationEvent> {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {

        /**
         * 触发时机: 环境准备完成,此时 Environment 中包含 系统变量,JVM参数,application.yml中的配置信息
         * 注意:
         *      该事件触发时,Context 容器还没有创建,因此通过 @Component 注解加入到容器中的方式不会触发该方法
         * 只有通过 Spring.factories 的形式才可以
         */
        if(event instanceof ApplicationEnvironmentPreparedEvent) {
            System.out.println("Spring内置事件 【ApplicationEnvironmentPreparedEvent】 事件触发, 事件源: " + event.getSource());
        }

        /**
         * 触发时机: Context 容器刚刚创建完毕,回调完所有的 ApplicationContextInitializer 之后
         * 注意:
         *      该事件触发时,Context 容器刚刚创建完毕,回调完所有的 ApplicationContextInitializer,此时并没有扫描创建容器中的Bean,因此通过
         * @Component 注解的形式无法触发该方法,只有通过 Spring.factories 的形式才可以
         */
        if(event instanceof ApplicationContextInitializedEvent) {
            System.out.println("Spring内置事件 【ApplicationContextInitializedEvent 】 事件触发, 事件源: " + event.getSource());
        }

        /**
         * 触发时机: Context 容器刚刚创建完毕,回调完所有的 ApplicationContextInitializer 之后,同时mainClass加载并封装为 BeanDefinition 注入到容器中
         * 注意:
         *      该事件触发时并没有扫描创建容器中的Bean,因此通过
         * @Component 注解的形式无法触发该方法,只有通过 Spring.factories 的形式才可以
         * 特别注意:
         *      如果你集成了 Spring-Cloud,其内部 org.springframework.cloud.context.restart.RestartListener 中监听了 ContextRefreshedEvent 事件，
         *      其对 ContextRefreshedEvent 事件的处理逻辑是发送了一个 ApplicationPreparedEvent 事件,此时容器已经刷新完毕了,因此可以监听到该事件。
         *
         */
        if(event instanceof ApplicationPreparedEvent) {
            System.out.println("Spring内置事件 【ApplicationPreparedEvent  】 事件触发, 事件源: " + event.getSource());
        }

        /**
         * 触发时机: ContextRefreshedEvent 事件在 refresh刷新完成时触发
         * 注意:
         *      由于此时容器已经刷新,所有Bean都被容器扫描且创建,因此可以通过 @Component 注解 或者 classpath:META-INF/spring.factories的形式加入
         */
        if(event instanceof ContextRefreshedEvent) {
            System.out.println("Spring内置事件 【ContextRefreshedEvent】 事件触发, 事件源: " + event.getSource());
        }

        /**
         * 触发时机: 容器刷新完毕,afterRefresh()之后,回调 CommandLineRunner 之前触发
         */
        if(event instanceof ApplicationStartedEvent) {
            System.out.println("Spring内置事件 【ApplicationStartedEvent  】 事件触发, 事件源: " + event.getSource());
        }

        /**
         * 触发时机: 容器刷新完毕,回调 CommandLineRunner 之后触发
         */
        if(event instanceof ApplicationReadyEvent) {
            System.out.println("Spring内置事件 【ApplicationReadyEvent   】 事件触发, 事件源: " + event.getSource());
        }

        // ContextStartedEvent 事件在所有单例bean创建完成后发布，先于 ContextRefreshedEvent 事件
        // 但是只有等 ConfigurableApplicationContext 的 start()方法执行后才会触发,暂时未知其触发方式
        if(event instanceof ContextStartedEvent) {
            System.out.println("Spring内置事件 【ContextStartedEvent】 事件触发, 事件源: " + event.getSource());
        }



        // ConfigurableApplicationContext的 close() 执行完成时发布，此时IOC容器已经完毕,但尚未销毁所有的bean
        if(event instanceof ContextClosedEvent) {
            System.out.println("Spring内置事件 【ContextClosedEvent】 事件触发, 事件源: " + event.getSource());
//            throw new RuntimeException("模拟处理异常");

        }

        // ConfigurableApplicationContext 的 stop() 执行完成时发布
        // 但是只有等 ConfigurableApplicationContext 的 stop()方法执行后才会触发,暂时未知其触发方式
        if(event instanceof ContextStoppedEvent) {
            System.out.println("Spring内置事件 【ContextStoppedEvent】 事件触发, 事件源: " + event.getSource());
        }

    }

}
